package com.sojson.util.init;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.springframework.core.io.Resource;

import com.sojson.base.impl.BaseUtilDto;
import com.sojson.config.exception.TransException;
import com.sojson.constant.ConstantByMap;
import com.sojson.constant.IpConfig;
import com.sojson.util.MavenUtil;
import com.sojson.util.StringUtil;
import com.sojson.util.file.FileUtil;
import com.sojson.util.init.interfaces.ICopyStaticFile;
import com.sojson.util.init.interfaces.ICreateDatabase;
import com.sojson.util.init.interfaces.IInitClassField;
import com.sojson.util.init.interfaces.IInitMethod;
import com.sojson.util.init.interfaces.IInitSystemProperties;
import com.sojson.util.init.interfaces.IRegisterDriver;
import com.sojson.util.properties.PropertiesUtil;

import cn.hutool.core.io.IoUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 应用程序初始化工具类
 * 
 * @author liu
 * @date 2021-05-18
 */
@Slf4j
public class InitUtil {

    /** 初始化方法 */
    private static List<IInitMethod> initMethodss = new ArrayList<>(10);
    /** 初始化系统属性 */
    private static List<IInitSystemProperties> initSystemPropertiess = new ArrayList<>(1);
    /** 给静态属性赋初值 */
    private static List<IInitClassField> initClassFields = new ArrayList<>(1);
    /** 加载一些驱动 */
    private static List<IRegisterDriver> registerDrivers = new ArrayList<>(1);
    /** 创建数据库 */
    private static List<ICreateDatabase> createDatabases = new ArrayList<>(1);
    /** 将静态文件复制到文件目录接口 */
    private static List<ICopyStaticFile> copyStaticFiles = new ArrayList<>(1);

    /**
     * 开始运行
     * @throws Exception 
     */
    @SuppressWarnings("unused")
    public static void run() {
        try {

            if (initMethodss.size() > 0) {
                for (IInitMethod initMethods : initMethodss) {
                    initMethods.run();
                }
            }

            // 初始化系统属性
            initSystemProperties();
            // 加载Properties文件属性
            PropertiesUtil.loadToSystem();
            // 初始化pom.xml信息
            MavenUtil.getPackaging();
            // 初始化项目路径
            String url = IpConfig.url;
            // 给静态属性赋初值
            initClassField();
            // 加载一些驱动
            registerDriver();
            // 创建项目缓存目录结构
            createFileStructure();
            // 创建数据库
            createDatabase();
            // 将静态文件复制到文件目录
            copyStaticFile();

        } catch (Exception e) {
            throw new TransException();
        } finally {
            initMethodss = null;
            initSystemPropertiess = null;
            initClassFields = null;
            registerDrivers = null;
            createDatabases = null;
            copyStaticFiles = null;
        }
    }

    /**
     * 初始化系统属性
     */
    public static void initSystemProperties() {
        // 开始给系统属性赋初值
        log.info("开始给系统属性赋初值！");

        // TimeZone.setDefault(TimeZone.getTimeZone("ETC/GMT-8"));
        if (initSystemPropertiess.size() > 0) {
            for (IInitSystemProperties initSystemProperties : initSystemPropertiess) {
                initSystemProperties.run();
            }
        }

        // 给系统属性赋初值完成
        log.info("给系统属性赋初值完成！");
    }

    /**
     * 给静态属性赋初值
     * 
     * @throws SQLException
     */
    private static void initClassField() throws SQLException {
        // 开始给静态属性赋初值
        log.info("正在给静态属性赋初值！");

        if (initClassFields.size() > 0) {
            for (IInitClassField initClassField : initClassFields) {
                initClassField.run();
            }
        }

        // 给静态属性赋初值完成
        log.info("给静态属性赋初值完成！");
    }

    /**
     * 加载一些驱动
     */
    private static void registerDriver() {
        // 开始加载驱动
        log.info("正在加载驱动类！");

        // 加载...驱动
        if (registerDrivers.size() > 0) {
            for (IRegisterDriver registerDriver : registerDrivers) {
                registerDriver.run();
            }
        }

        // 加载完成
        log.info("驱动类加载完成！");
    }

    /**
     * 创建项目缓存目录结构
     */
    private static void createFileStructure() {
        // 开始创建目录
        log.info("正在创建目录结构！");
        FileUtil.createFileStructure();
        // 目录创建完成
        log.info("目录结构创建完成！");
    }

    /**
     * 创建数据库
     */
    private static void createDatabase() {
        // 开始创建数据库
        log.info("正在创建数据库！");

        // DBUtils.createDatabase();
        if (createDatabases.size() > 0) {
            for (ICreateDatabase createDatabase : createDatabases) {
                createDatabase.run();
            }
        }

        // 数据库创建完成
        log.info("数据库创建完成！");
    }

    /**
     * 将静态文件复制到指定目录
     */
    private static void copyStaticFile() throws IOException {
        log.info("正在将静态文件复制到指定目录！");

        // 初始化要复制的文件列表集合
        List<FileCopy> fileCopies = new ArrayList<>();
        // 初始化要复制的文件
        copyStaticFileInit(fileCopies);
        if (copyStaticFiles.size() > 0) {
            for (ICopyStaticFile copyStaticFile : copyStaticFiles) {
                copyStaticFile.run();
            }
        }

        for (FileCopy fileCopy : fileCopies) {
            // 获取资源文件或目录的路径
            String resourcePath = fileCopy.getResource();
            // 如果文件或目录不存在就跳过
            if (!PropertiesUtil.getResource(resourcePath.replace(ConstantByMap.CLASSPATHX_, ConstantByMap.CLASSPATH_))
                .exists()) {
                continue;
            }
            // 获取资源文件或资源文件目录下的文件
            List<Resource> resources = PropertiesUtil.getResources(resourcePath + (fileCopy.getIsFile() ? "" : "**"));
            // 遍历并复制文件到指定目录
            for (Resource resource : resources) {
                // 获取资源文件的全路径
                String path = resource.getURL().getPath();
                // 获取资源文件的资源路径
                path = MavenUtil.getFileResourcePath(path);
                // 去掉文件开头的路径(去掉基础路径)
                path = path.substring(fileCopy.getFileName().length() + 1);
                // 将路径解码(读取到的空格会转为%20,所以需要解码)
                path = StringUtil.decode(path);
                // 如果是目录就跳过
                if (path.endsWith("/") || path.endsWith("\\")) {
                    continue;
                }

                // 如果文件存在就跳过
                File file = new File(fileCopy.getTarget() + path);
                if (file.exists()) {
                    continue;
                }
                // 如果文件不存在就检查目录是否存在,不存在就创建
                File parentFile = file.getParentFile();
                if (!parentFile.exists()) {
                    parentFile.mkdirs();
                }

                // 复制文件到指定目录
                InputStream inputStream = null;
                FileOutputStream fileOutputStream = null;
                try {
                    inputStream = resource.getInputStream();
                    fileOutputStream = new FileOutputStream(file);
                    IoUtil.copy(inputStream, fileOutputStream);
                    fileOutputStream.flush();
                } catch (Exception e) {
                    log.error(e.getMessage());
                } finally {
                    FileUtil.closeStream(inputStream, fileOutputStream);
                }
            }
        }

        log.info("将静态文件复制到指定目录完成！");
    }

    /**
     * 将静态文件复制到指定目录(结尾加/为目录,否则为文件)
     */
    private static void copyStaticFileInit(List<FileCopy> fileCopies) throws IOException {

        // 资源文件的目录
        FileCopy target = InitUtil.FileCopy.resourceClasspath("static/");
        // 要复制到的目录
        target.target(ConstantByMap.LOG_PATH_FILE_SYSTEM_THIS_DIRECTORY_BUCKET_NAME_BASE);
        // 添加到集合中
        fileCopies.add(target);

        // 资源文件的目录
        target = InitUtil.FileCopy.resourceClasspath(ConstantByMap.CERT_CLASSPATH_FILE_BASE_PATH + "/");
        // 要复制到的目录
        target.target(ConstantByMap.CERT_PATH);
        // 添加到集合中
        fileCopies.add(target);

    }

    public static class FileCopy extends BaseUtilDto {

        /***/
        private static final long serialVersionUID = -3434892693094099760L;
        /** 是否是文件(true:文件,false:目录) */
        private boolean isFile;
        /** 文件名 */
        private String fileName;
        /** 源目录 */
        private String resource;
        /** 目标目录 */
        private String target;

        /** 获取是否是文件(true:文件,false:目录) */
        public boolean getIsFile() {
            return isFile;
        }

        /** 设置是否是文件(true:文件,false:目录) */
        public void setIsFile(boolean isFile) {
            this.isFile = isFile;
        }

        /** 获取文件名 */
        public String getFileName() {
            return fileName;
        }

        /** 设置文件名 */
        public void setFileName(String fileName) {
            if (fileName.startsWith("/") || fileName.startsWith("\\")) {
                if (fileName.startsWith("/")) {
                    fileName = fileName.substring(1);
                }
                if (fileName.startsWith("\\")) {
                    fileName = fileName.substring(2);
                }
            }
            if (fileName.endsWith("/") || fileName.endsWith("\\")) {
                setIsFile(false);
                if (fileName.endsWith("/")) {
                    fileName = fileName.substring(0, fileName.length() - 1);
                }
                if (fileName.endsWith("\\")) {
                    fileName = fileName.substring(0, fileName.length() - 2);
                }
            } else {
                setIsFile(true);
            }
            this.fileName = fileName;
        }

        /** 获取源目录 */
        public String getResource() {
            return resource;
        }

        /** 设置源目录 */
        public void setResource(String resource) {
            setFileName(resource);
            this.resource = resource;
        }

        /** 设置源目录(开头为classpath:) */
        public void setResourceClasspath(String resource) {
            setFileName(resource);
            this.resource = ConstantByMap.CLASSPATHX_ + resource;
        }

        /** 获取目标目录 */
        public String getTarget() {
            return target;
        }

        /** 设置目标目录 */
        public void setTarget(String target) {
            this.target = target;
        }

        /**
         * 设置源目录
         */
        public static FileCopy resource(String resource) {
            FileCopy fileCopy = new FileCopy();
            fileCopy.setResource(resource);
            return fileCopy;
        }

        /**
         * 设置源目录
         */
        public static FileCopy resourceClasspath(String resource) {
            FileCopy fileCopy = new FileCopy();
            fileCopy.setResourceClasspath(resource);
            return fileCopy;
        }

        /**
         * 设置目标目录
         */
        public FileCopy target(String target) {
            setTarget(target);
            return this;
        }

    }

    /**
     * 添加初始化系统属性处理器
     */
    public static void add(IInitMethod initMethod) {
        InitUtil.initMethodss.add(initMethod);
    }

    /**
     * 添加初始化系统属性处理器
     */
    public static void add(IInitSystemProperties initSystemProperties) {
        InitUtil.initSystemPropertiess.add(initSystemProperties);
    }

    /**
     * 添加给静态属性赋初值处理器
     */
    public static void add(IInitClassField initClassField) {
        InitUtil.initClassFields.add(initClassField);
    }

    /**
     * 添加加载一些驱动处理器
     */
    public static void add(IRegisterDriver registerDriver) {
        InitUtil.registerDrivers.add(registerDriver);
    }

    /**
     * 添加创建数据库处理器
     */
    public static void add(ICreateDatabase createDatabase) {
        InitUtil.createDatabases.add(createDatabase);
    }

    /**
     * 添加将静态文件复制到文件目录处理器
     */
    public static void add(ICopyStaticFile copyStaticFile) {
        InitUtil.copyStaticFiles.add(copyStaticFile);
    }

}